package io.gitee.zhangbinhub.acp.core.file.pdf

import com.itextpdf.html2pdf.ConverterProperties
import com.itextpdf.html2pdf.HtmlConverter
import com.itextpdf.text.Document
import com.itextpdf.text.Image
import com.itextpdf.text.PageSize
import com.itextpdf.text.pdf.*
import io.gitee.zhangbinhub.acp.core.common.CommonTools.doDeleteFile
import io.gitee.zhangbinhub.acp.core.common.CommonTools.getFileContent
import io.gitee.zhangbinhub.acp.core.common.CommonTools.isNullStr
import io.gitee.zhangbinhub.acp.core.common.log.LogFactory.Companion.getInstance
import io.gitee.zhangbinhub.acp.core.file.pdf.fonts.FontLoader
import io.gitee.zhangbinhub.acp.core.file.template.TemplateService
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream


class PDFService {
    private val log = getInstance(this.javaClass)

    /**
     * HTML页面转为PDF
     *
     * @param htmlStr  html页面源码,必须完整
     * @param fileName 文件名
     * @param basePath 图片basePath
     * @return PDF文件绝对路径
     */
    @JvmOverloads
    fun htmlToPdf(
        htmlStr: String, fileName: String, basePath: String?,
        ttcFontPath: String? = null,
        ttcFontIndex: Int = 0
    ): String {
        var os: OutputStream? = null
        return try {
            os = FileOutputStream(fileName)
            //添加中文字体支持
            val properties = ConverterProperties()
            properties.baseUri = basePath
            properties.setFontProvider(FontLoader.getFontProvider(ttcFontPath, ttcFontIndex))
            HtmlConverter.convertToPdf(htmlStr, os, properties)
            os.close()
            fileName
        } catch (e: Exception) {
            log.error("generate PDF Exception:" + e.message)
            try {
                os?.close()
                val file = File(fileName)
                doDeleteFile(file, false)
            } catch (ex: Exception) {
                log.error(ex.message, ex)
            }
            ""
        }
    }

    /**
     * HTML页面转为PDF
     *
     * @param htmlFilePath html文件绝对路径
     * @param fileName     文件名
     * @param basePath     图片文件所在路径
     * @return PDF文件绝对路径
     */
    @JvmOverloads
    fun htmlFileToPdf(
        htmlFilePath: String, fileName: String, basePath: String?,
        ttcFontPath: String? = null,
        ttcFontIndex: Int = 0
    ): String = htmlToPdf(getFileContent(htmlFilePath), fileName, basePath, ttcFontPath, ttcFontIndex)

    /**
     * HTML页面转为PDF
     *
     * @param fileName     文件名
     * @param templatePath 模板文件路径
     * @param data         填充模板数据项
     * @return PDF文件绝对路径
     */
    @JvmOverloads
    fun htmlToPdfForTemplate(
        fileName: String, templatePath: String, data: Map<String, Any?>?,
        ttcFontPath: String? = null,
        ttcFontIndex: Int = 0
    ): String =
        htmlToPdf(TemplateService.generateString(templatePath, data), fileName, null, ttcFontPath, ttcFontIndex)

    /**
     * 图片转PDF,边距单位（磅）
     *
     * @param imageFileNames 图片文件，绝对路径
     * @param resultFile     结果文件绝对路径
     * @param flag           0-方案一,1-方案二
     * @param isHorizontal   是否水平
     * @param left           左边距单位（磅）
     * @param right          右边距单位（磅）
     * @param top            上边距单位（磅）
     * @param bottom         下边距单位（磅）
     * @return PDF文件绝对路径
     */
    fun imageToPdf(
        imageFileNames: Array<String>,
        resultFile: String,
        flag: Int,
        isHorizontal: Boolean,
        left: Float,
        right: Float,
        top: Float,
        bottom: Float
    ): String = if (imageFileNames.isNotEmpty()) {
        val doc: Document = if (isHorizontal) {
            Document(
                PageSize.A4.rotate(), left, right, top, bottom
            )
        } else {
            Document(PageSize.A4, left, right, top, bottom)
        }
        try {
            PdfWriter.getInstance(doc, FileOutputStream(resultFile))
            doc.open()
            for (imageFileName in imageFileNames) {
                doc.newPage()
                val image = Image.getInstance(imageFileName)
                val dpi = 72f // itext 中dpi为72px/英寸
                val height = image.height / dpi * 25.4f
                val width = image.width / dpi * 25.4f
                val percent: Int = when (flag) {
                    0 -> getPercent(height, width, isHorizontal)
                    1 -> getPercent2(width, isHorizontal)
                    else -> getPercent(height, width, isHorizontal)
                }
                image.alignment = Image.MIDDLE
                image.scalePercent(percent.toFloat())
                doc.add(image)
            }
            doc.close()
            resultFile
        } catch (e: Exception) {
            doc.close()
            log.error("image to PDF Exception:" + e.message, e)
            ""
        }
    } else {
        ""
    }

    /**
     * PDF增加页面事件
     *
     * @param pdfFilePath    PDF文件路径，绝对路径或相对路径
     * @param resultFileName 目标文件绝对路径
     * @param event          事件类
     * @param isDeleteFile   是否删除源文件
     * @param orientation    0-自动 1-纵向 2-横向
     * @return PDF文件绝对路径
     */
    fun pdfAddPageEvent(
        pdfFilePath: String, resultFileName: String, event: PdfPageEventHelper?, isDeleteFile: Boolean, orientation: Int
    ): String {
        var document: Document? = null
        return try {
            val reader = PdfReader(pdfFilePath, "PDF".toByteArray())
            document = if (orientation == 0 || orientation == 1) {
                Document()
            } else {
                Document(PageSize.A4.rotate())
            }
            val writer = PdfWriter.getInstance(document, FileOutputStream(resultFileName))
            writer.pageEvent = event
            document.open()
            var page: PdfImportedPage?
            for (i in 1..reader.numberOfPages) {
                page = writer.getImportedPage(reader, i)
                val image = Image.getInstance(page)
                val readWidth = reader.getPageSize(i).width.toInt()
                val writeWidth = writer.pageSize.width.toInt()
                if (orientation == 0 || orientation == 1) {
                    if (writeWidth + 10 < readWidth) image.setRotationDegrees(90f)
                } else {
                    if (writeWidth > readWidth + 10) image.setRotationDegrees(90f)
                }
                image.setAbsolutePosition(0f, 0f)
                document.add(image)
                document.newPage()
            }
            document.close()
            if (isDeleteFile) {
                doDeleteFile(File(pdfFilePath), false)
            }
            log.info("add page number success")
            resultFileName
        } catch (e: Exception) {
            try {
                document?.close()
            } catch (ex: Exception) {
                log.error("add page number Exception:" + ex.message, ex)
            }
            log.error("add page number Exception:" + e.message, e)
            ""
        }
    }

    /**
     * 给PDF增加水印
     *
     * @param pdfFilePath    pdf源文件
     * @param waterMarkPath  水印图片
     * @param resultFileName 目标文件名
     * @param isDeleteFile   是否删除源文件
     * @return PDF文件绝对路径
     */
    fun pdfAddWaterMark(
        pdfFilePath: String, waterMarkPath: String, resultFileName: String, isDeleteFile: Boolean
    ): String = pdfEncrypt(
        pdfFilePath,
        waterMarkPath,
        resultFileName,
        true,
        null,
        PDF_OWNER_PASSWORD,
        PdfWriter.ALLOW_COPY or PdfWriter.ALLOW_PRINTING,
        isDeleteFile
    )

    /**
     * PDF加密
     *
     * @param pdfFilePath    pdf源文件
     * @param waterMarkPath  水印图片路径,为空则表示不增加水印
     * @param resultFileName 目标文件名
     * @param isEncrypt      是否进行文档加密
     * @param userPassword   打开文档时需输入的访问密码
     * @param ownerPassword  文档的设置许可密码
     * @param permissions    允许的权限
     * @param isDeleteFile   是否删除源文件
     * @return PDF文件绝对路径
     */
    fun pdfEncrypt(
        pdfFilePath: String,
        waterMarkPath: String,
        resultFileName: String,
        isEncrypt: Boolean,
        userPassword: String?,
        ownerPassword: String?,
        permissions: Int,
        isDeleteFile: Boolean
    ): String {
        var stamp: PdfStamper? = null
        var img: Image? = null
        return try {
            if (!isNullStr(waterMarkPath)) {
                img = Image.getInstance(waterMarkPath)
            }/* 重构文件路径 end **/
            val reader = PdfReader(pdfFilePath, "PDF".toByteArray())
            stamp = PdfStamper(reader, FileOutputStream(resultFileName))
            if (isEncrypt) {/* 加密PDF,只允许查看和复制 **/
                stamp.setEncryption(
                    PdfWriter.STANDARD_ENCRYPTION_40, userPassword, ownerPassword, permissions
                )
            }
            for (i in 1..reader.numberOfPages) {
                if (img != null) {
                    val under = stamp.getUnderContent(i)
                    val width = reader.getPageSize(i).width.toInt()/* 设置水印图片插入的绝对位置 **/
                    img.setAbsolutePosition(0f, 0f)
                    if (width > img.width.toInt()) {/* 旋转水印图片 **/
                        img.setRotationDegrees(-90f)/* 插入水印 **/
                        under.addImage(img)
                        img.setRotationDegrees(0f)
                    } else {
                        under.addImage(img)
                    }
                }
            }
            stamp.close() // 关闭
            if (isDeleteFile) {
                doDeleteFile(File(pdfFilePath), false)
            }
            log.info("PDF encrypt success")
            resultFileName
        } catch (e: Exception) {
            try {
                stamp?.close()
            } catch (ex: Exception) {
                log.error("PDF encrypt Exception:" + ex.message, ex)
            }
            log.error("PDF encrypt Exception:" + e.message, e)
            ""
        }
    }

    /**
     * 合并多个PDF为一个
     *
     * @param fileNames      源文件，绝对路径或相对路径
     * @param resultFileName 目标文件绝对路径
     * @param isDeleteFile   是否删除源文件
     * @return PDF文件绝对路径
     */
    fun pdfToMerge(fileNames: Array<String>, resultFileName: String, isDeleteFile: Boolean): String {
        return if (fileNames.isNotEmpty()) {
            var document: Document? = null
            try {
                val filename = fileNames[0]
                document = Document(PdfReader(filename).getPageSize(1))
                val copy = PdfCopy(
                    document, FileOutputStream(
                        resultFileName
                    )
                )
                document.open()
                for (fileName in fileNames) {
                    val reader = PdfReader(filename)
                    val n = reader.numberOfPages
                    for (j in 1..n) {
                        document.newPage()
                        val page = copy.getImportedPage(reader, j)
                        copy.addPage(page)
                    }
                    if (isDeleteFile) {
                        doDeleteFile(File(filename), false)
                    }
                }
                document.close()
                log.info("merge PDF success")
                resultFileName
            } catch (e: Exception) {
                document?.close()
                log.error("merge PDF Exception:" + e.message, e)
                ""
            }
        } else {
            ""
        }
    }

    /**
     * 第一种解决方案 在不改变图片形状的同时,判断,如果h>w,则按h压缩,否则在w>h或w=h的情况下,按宽度压缩
     *
     * @param h 高
     * @param w 宽
     * @return 结果
     */
    private fun getPercent(h: Float, w: Float, isHorizontal: Boolean): Int {
        val p: Int
        val p2: Float = if (isHorizontal) {
            if (h > w) {
                210 / h * 100
            } else {
                297 / w * 100
            }
        } else {
            if (h > w) {
                297 / h * 100
            } else {
                210 / w * 100
            }
        }
        p = Math.round(p2)
        return p
    }

    /**
     * 第二种解决方案,统一按照宽度压缩 这样来的效果是,所有图片的宽度是相等的
     *
     * @param w 宽
     * @return 结果
     */
    private fun getPercent2(w: Float, isHorizontal: Boolean): Int {
        val p: Int
        val p2: Float = if (isHorizontal) {
            297 / w * 100
        } else {
            210 / w * 100
        }
        p = Math.round(p2)
        return p
    }

    companion object {
        /**
         * 拥有者密码
         */
        var PDF_OWNER_PASSWORD = "123456"
    }
}
